package core

import (
	"bytes"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"chainmaker.org/chainmaker/common/v2/cert"
	"chainmaker.org/chainmaker/common/v2/crypto"
	"chainmaker.org/chainmaker/common/v2/crypto/asym"
	"chainmaker.org/chainmaker/common/v2/evmutils"
	"github.com/tjfoc/gmsm/sm3"
)

//--------------------------------------------------------------------
// 常量与类型定义
//--------------------------------------------------------------------

// 支持的算法类型
const (
	AlgoSM2       = "SM2"
	AlgoECDSAP256 = "ECDSA_P256"
)

//--------------------------------------------------------------------
// 密钥操作基础功能
//--------------------------------------------------------------------

// GenerateKeyPair 生成密钥对，返回PEM格式
func GenerateKeyPair(algo string) (string, string, error) {
	var keyType crypto.KeyType
	switch algo {
	case AlgoSM2:
		keyType = crypto.SM2
	case AlgoECDSAP256:
		keyType = crypto.ECC_NISTP256
	default:
		err := fmt.Errorf("unsupported algorithm: %s", algo)
		LogError("GenerateKeyPair: " + err.Error())
		return "", "", err
	}
	priv, err := asym.GenerateKeyPair(keyType)
	if err != nil {
		LogError("Failed to generate key pair: " + err.Error())
		return "", "", err
	}
	priPEM, err := priv.String()
	if err != nil {
		LogError("Failed to export private key PEM: " + err.Error())
		return "", "", err
	}
	pubPEM, err := priv.PublicKey().String()
	if err != nil {
		LogError("Failed to export public key PEM: " + err.Error())
		return "", "", err
	}
	return pubPEM, priPEM, nil
}

// Sign 使用私钥签名数据
func Sign(priKey, algo string, data []byte) ([]byte, error) {
	var hashType crypto.HashType
	switch algo {
	case AlgoSM2:
		hashType = crypto.HASH_TYPE_SM3
	case AlgoECDSAP256:
		hashType = crypto.HASH_TYPE_SHA256
	default:
		err := fmt.Errorf("unsupported algorithm: %s", algo)
		LogError("Sign: " + err.Error())
		return nil, err
	}
	priv, err := asym.PrivateKeyFromPEM([]byte(priKey), nil)
	if err != nil {
		LogError("Failed to parse private key PEM: " + err.Error())
		return nil, err
	}
	opts := crypto.SignOpts{
		Hash: hashType,
		UID:  crypto.CRYPTO_DEFAULT_UID,
	}
	sig, err := priv.SignWithOpts(data, &opts)
	if err != nil {
		LogError("Failed to sign data: " + err.Error())
		return nil, err
	}
	return sig, nil
}

// Verify 使用公钥验证签名
func Verify(pubKey, algo string, data, signature []byte) (bool, error) {
	var hashType crypto.HashType
	switch algo {
	case AlgoSM2:
		hashType = crypto.HASH_TYPE_SM3
	case AlgoECDSAP256:
		hashType = crypto.HASH_TYPE_SHA256
	default:
		err := fmt.Errorf("unsupported algorithm: %s", algo)
		LogError("Verify: " + err.Error())
		return false, err
	}
	pub, err := asym.PublicKeyFromPEM([]byte(pubKey))
	if err != nil {
		LogError("Failed to parse public key PEM: " + err.Error())
		return false, err
	}
	opts := crypto.SignOpts{
		Hash: hashType,
		UID:  crypto.CRYPTO_DEFAULT_UID,
	}
	ok, err := pub.VerifyWithOpts(data, signature, &opts)
	if err != nil {
		LogError("Failed to verify signature: " + err.Error())
		return false, err
	}
	return ok, nil
}

//--------------------------------------------------------------------
// JSON规范化与处理
//--------------------------------------------------------------------

// 规范化JSON为确定性格式
func canonicalizeJSON(jsonData []byte) ([]byte, error) {
	var obj interface{}
	decoder := json.NewDecoder(bytes.NewReader(jsonData))
	decoder.UseNumber() // 保持数字精度
	if err := decoder.Decode(&obj); err != nil {
		LogError("Failed to decode JSON: " + err.Error())
		return nil, err
	}

	// 规范化递归处理
	normalized := canonicalizeValue(obj)

	// 无缩进、按键排序的JSON
	return json.Marshal(normalized)
}

// 递归规范化JSON值
func canonicalizeValue(v interface{}) interface{} {
	switch v := v.(type) {
	case map[string]interface{}:
		// 处理对象：按键排序
		keys := make([]string, 0, len(v))
		for k := range v {
			keys = append(keys, k)
		}
		sort.Strings(keys)

		result := make(map[string]interface{})
		for _, k := range keys {
			result[k] = canonicalizeValue(v[k])
		}
		return result

	case []interface{}:
		// 处理数组：处理每个元素
		result := make([]interface{}, len(v))
		for i, item := range v {
			result[i] = canonicalizeValue(item)
		}
		return result

	default:
		// 基本类型直接返回
		return v
	}
}

//--------------------------------------------------------------------
// Base64URL编码相关
//--------------------------------------------------------------------

// base64UrlEncode 对数据进行Base64Url编码
func base64UrlEncode(data []byte) string {
	// 使用标准base64编码
	encoded := base64.StdEncoding.EncodeToString(data)
	// 替换为URL安全字符
	encoded = strings.ReplaceAll(encoded, "+", "-")
	encoded = strings.ReplaceAll(encoded, "/", "_")
	// 去掉填充的等号
	encoded = strings.TrimRight(encoded, "=")
	return encoded
}

// base64UrlDecode 对Base64Url编码的数据进行解码
func base64UrlDecode(encoded string) ([]byte, error) {
	// 添加必要的填充
	paddingNeeded := 4 - (len(encoded) % 4)
	if paddingNeeded < 4 {
		encoded += strings.Repeat("=", paddingNeeded)
	}

	// 替换URL安全字符为标准Base64字符
	encoded = strings.ReplaceAll(encoded, "-", "+")
	encoded = strings.ReplaceAll(encoded, "_", "/")

	// 解码
	return base64.StdEncoding.DecodeString(encoded)
}

//--------------------------------------------------------------------
// 区块链地址生成相关
//--------------------------------------------------------------------

// GetAddresses 返回多链格式的地址（zxl、chainmaker、ethereum）
// algo: "SM2" 或 "ECDSA_P256" 等
func GetAddresses(pubKeyPEM string, algo string) (map[string]string, error) {
	addresses := make(map[string]string)
	var hashType crypto.HashType

	// 根据算法类型选择哈希算法
	if algo == "SM2" {
		hashType = crypto.HASH_TYPE_SM3
	} else {
		hashType = crypto.HASH_TYPE_SHA256
	}

	// 解析公钥
	publicKey, err := asym.PublicKeyFromPEM([]byte(pubKeyPEM))
	if err != nil {
		return nil, fmt.Errorf("解析公钥失败: %v", err)
	}

	// ZXL链地址 - 注意：ZXL地址始终使用SM3哈希，不依赖于hashType
	if zxlAddr, err := pkToZXLAddrStr(publicKey); err == nil {
		addresses["zxl"] = zxlAddr
	}

	// 长安链地址
	if cmAddr, err := pkToCMAddrStr(publicKey, hashType); err == nil {
		addresses["chainmaker"] = cmAddr
	}

	// 以太坊地址
	if ethAddr, err := pkToETHAddrStr(publicKey); err == nil {
		addresses["ethereum"] = ethAddr
	}

	return addresses, nil
}

// pkToZXLAddrStr 根据公钥生成ZXL地址字符串，始终使用SM3哈希
func pkToZXLAddrStr(pk crypto.PublicKey) (string, error) {
	pkBytes, err := evmutils.MarshalPublicKey(pk)
	if err != nil {
		return "", err
	}

	// 使用SM3哈希算法
	sm3Hash := sm3.New()
	sm3Hash.Write(pkBytes)
	hashBytes := sm3Hash.Sum(nil)

	// 取后20字节作为地址
	addrBytes := hashBytes[len(hashBytes)-20:]
	return hex.EncodeToString(addrBytes), nil
}

// pkToCMAddrStr 根据公钥生成长安链地址字符串
func pkToCMAddrStr(pk crypto.PublicKey, hashType crypto.HashType) (string, error) {
	ski, err := cert.ComputeSKI(hashType, pk.ToStandardKey())
	if err != nil {
		return "", err
	}

	// 使用Keccak256哈希
	bytesAddr := evmutils.Keccak256(ski)
	addr := hex.EncodeToString(bytesAddr)[24:] // 取后20字节
	return addr, nil
}

// pkToETHAddrStr 根据公钥生成以太坊地址字符串
func pkToETHAddrStr(pk crypto.PublicKey) (string, error) {
	pkBytes, err := evmutils.MarshalPublicKey(pk)
	if err != nil {
		return "", err
	}

	// 使用Keccak256哈希，去掉公钥第一个字节
	bytesAddr := evmutils.Keccak256(pkBytes[1:])
	addr := hex.EncodeToString(bytesAddr)[24:] // 取后20字节
	return addr, nil
}

//--------------------------------------------------------------------
// JWT数据处理相关
//--------------------------------------------------------------------

// SignData 对JWT格式数据进行签名
// 接收headerJson和payloadJson，从header中提取算法类型，对header.payload格式进行签名
func SignData(priKey string, headerJson, payloadJson string) ([]byte, error) {
	// 解析header以获取算法类型
	var header map[string]interface{}
	if err := json.Unmarshal([]byte(headerJson), &header); err != nil {
		return nil, fmt.Errorf("解析header失败: %v", err)
	}

	// 从header中获取算法类型
	algValue, ok := header["alg"].(string)
	if !ok {
		return nil, fmt.Errorf("header中缺少有效的alg字段")
	}

	// 映射JWT算法到内部算法
	var algo string
	switch algValue {
	case "SM2":
		algo = AlgoSM2
	case "ES256":
		algo = AlgoECDSAP256
	default:
		return nil, fmt.Errorf("不支持的算法: %s", algValue)
	}

	// 规范化JSON
	headerCanonical, err := canonicalizeJSON([]byte(headerJson))
	if err != nil {
		return nil, fmt.Errorf("规范化header失败: %v", err)
	}

	payloadCanonical, err := canonicalizeJSON([]byte(payloadJson))
	if err != nil {
		return nil, fmt.Errorf("规范化payload失败: %v", err)
	}

	// Base64Url编码规范化后的header和payload
	headerBase64 := base64UrlEncode(headerCanonical)
	payloadBase64 := base64UrlEncode(payloadCanonical)

	// 构建签名内容: header.payload
	signContent := fmt.Sprintf("%s.%s", headerBase64, payloadBase64)

	// 签名
	return Sign(priKey, algo, []byte(signContent))
}

// VerifyData 验证JWT格式数据的签名
func VerifyData(pubKey string, headerJson, payloadJson string, signature []byte) (bool, error) {
	// 解析header以获取算法类型
	var header map[string]interface{}
	if err := json.Unmarshal([]byte(headerJson), &header); err != nil {
		return false, fmt.Errorf("解析header失败: %v", err)
	}

	// 从header中获取算法类型
	algValue, ok := header["alg"].(string)
	if !ok {
		return false, fmt.Errorf("header中缺少有效的alg字段")
	}

	// 映射JWT算法到内部算法
	var algo string
	switch algValue {
	case "SM2":
		algo = AlgoSM2
	case "ES256":
		algo = AlgoECDSAP256
	default:
		return false, fmt.Errorf("不支持的算法: %s", algValue)
	}

	// 规范化JSON
	headerCanonical, err := canonicalizeJSON([]byte(headerJson))
	if err != nil {
		return false, fmt.Errorf("规范化header失败: %v", err)
	}

	payloadCanonical, err := canonicalizeJSON([]byte(payloadJson))
	if err != nil {
		return false, fmt.Errorf("规范化payload失败: %v", err)
	}

	// Base64Url编码规范化后的header和payload
	headerBase64 := base64UrlEncode(headerCanonical)
	payloadBase64 := base64UrlEncode(payloadCanonical)

	// 构建签名内容: header.payload
	signContent := fmt.Sprintf("%s.%s", headerBase64, payloadBase64)

	// 验证签名
	return Verify(pubKey, algo, []byte(signContent), signature)
}
